package com.heima.article.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.heima.article.mapper.ApArticleConfigMapper;
import com.heima.article.mapper.ApArticleContentMapper;
import com.heima.article.mapper.ApArticleMapper;
import com.heima.article.service.ApArticleService;
import com.heima.article.service.ArticleFreemarkerService;
import com.heima.article.service.HotArticleService;
import com.heima.common.redis.CacheService;
import com.heima.model.article.pojos.ApArticle;
import com.heima.model.article.pojos.ApArticleConfig;
import com.heima.model.article.pojos.ApArticleContent;
import com.heima.model.common.constants.ArticleConstants;
import com.heima.model.common.constants.BeHaviorConstants;
import com.heima.model.common.constants.HotArticleConstants;
import com.heima.model.common.dtos.app.*;
import com.heima.model.common.dtos.common.PageResponseResult;
import com.heima.model.common.dtos.common.ResponseResult;
import com.heima.model.common.enums.AppHttpCodeEnum;
import com.heima.model.common.mess.ArticleVisitStreamMess;
import com.heima.model.common.mess.UpdateArticleMess;
import com.heima.model.common.vo.ArticleInfoVo;
import com.heima.model.common.vo.HotApArticleVo;
import com.heima.model.common.vo.SearchArticleVo;
import com.heima.utils.thread.AppThreadLocalUtil;
import org.apache.commons.lang.StringUtils;

import org.apache.commons.net.nntp.Article;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.kafka.core.KafkaTemplate;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.stream.Collectors;

/**
* @author Fu319
* @description 针对表【ap_article(文章信息表，存储已发布的文章)】的数据库操作Service实现
* @createDate 2025-03-31 15:22:12
*/
@Service
@Transactional
public class ApArticleServiceImpl extends ServiceImpl<ApArticleMapper, ApArticle> implements ApArticleService {
    public static final Integer MAX_PAGE_NUM = 50;
    @Autowired
    private ApArticleMapper apArticleMapper;
    @Autowired
    private ApArticleContentMapper apArticleContentMapper;
    @Autowired
    private ApArticleConfigMapper apArticleConfigMapper;
    @Autowired
    private ArticleFreemarkerService articleFreemarkerService;
    @Autowired
    private KafkaTemplate<String, String> kafkaTemplate;
    @Autowired
    private CacheService cacheService;
    /**
     * 加载首页信息
     * @param articleHomeDto 首页请求参数
     * @param localType 1为加载更多  2为加载最新
     * @return
     */
    @Override
    public ResponseResult load(ArticleHomeDto articleHomeDto,Short localType) {
            //校验参数
            Integer size = articleHomeDto.getSize();
            if(size == null|| size <= 0){
                size=10;
            }
            //保证每页最大条数不超过50
            size = Math.min(MAX_PAGE_NUM, size);
            articleHomeDto.setSize(size);
            //校验参数-->loalType
            //如果既不是查询最多也不是查询最新,则设置为查询最多
            if(!localType.equals(ArticleConstants.LOADTYPE_LOAD_MORE) &&!localType.equals(ArticleConstants.LOADTYPE_LOAD_NEW)){
                localType = ArticleConstants.LOADTYPE_LOAD_MORE;
            }
            //频道参数校验
            if(StringUtils.isBlank(articleHomeDto.getTag())){
                articleHomeDto.setTag(ArticleConstants.DEFAULT_TAG);
            }
            //时间校验
            if(articleHomeDto.getMaxBehotTime()==null){
                articleHomeDto.setMaxBehotTime(new Date());
            }
            if(articleHomeDto.getMinBehotTime()==null){
                articleHomeDto.setMinBehotTime(new Date());
            }
            //封装响应参数
            //List<ApArticle> articleList =apArticleMapper.loadArticleList(articleHomeDto,localType);
        List<ApArticle> apArticles = new ArrayList<>();
        String json = cacheService.get(ArticleConstants.HOT_ARTICLE_FIRST_PAGE + articleHomeDto.getTag());
        List<ApArticle> apArticleList = JSON.parseArray(json, ApArticle.class);
        for (ApArticle apArticle : apArticleList) {
            if(localType.equals(ArticleConstants.LOADTYPE_LOAD_MORE)){
                if(apArticle.getPublishTime().before(articleHomeDto.getMinBehotTime())){
                    apArticles.add(apArticle);
                }
            }
            if(localType.equals(ArticleConstants.LOADTYPE_LOAD_NEW)){
                if(apArticle.getPublishTime().after(articleHomeDto.getMaxBehotTime())){
                    apArticles.add(apArticle);
                }
            }
        }
        return ResponseResult.okResult(apArticles);

    }

    /**
     * app端保存和修改文章
     * @param articleDto
     * @return
     */
    @Override
    public ResponseResult saveArticle(ArticleDto articleDto) {
        //检查参数
        if(articleDto==null){
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_REQUIRE);
        }
        ApArticle apArticle = new ApArticle();
        //属性拷贝
        BeanUtils.copyProperties(articleDto,apArticle);
        //定义app端保存时间
        apArticle.setCreatedTime(new Date());
        //判断是否存在id
        if(articleDto.getId()==null){
            //不存在则新增文章信息和文章配置信息，文章内容信息
            //保存文章信息
            apArticleMapper.insert(apArticle);
            //保存文章配置信息
            ApArticleConfig apArticleConfig =new ApArticleConfig(apArticle.getId());
            apArticleConfigMapper.insert(apArticleConfig);
            //保存文章内容信息
            ApArticleContent apArticleContent =ApArticleContent.builder().articleId(apArticle.getId()).content(articleDto.getContent()).build();
            apArticleContentMapper.insert(apArticleContent);
            articleFreemarkerService.buildArticleToMinio(apArticle,articleDto.getContent());
        }else {
            //存在则修改文章信息 文章内容 修改文章配置
            apArticleMapper.updateById(apArticle);
            //修改文章内容
            if (articleDto.getContent() != null) {
                //查询文章内容信息
                LambdaQueryWrapper<ApArticleContent> wrapper = new LambdaQueryWrapper<>();
                wrapper.eq(ApArticleContent::getArticleId, apArticle.getId());
                ApArticleContent apArticleContent = apArticleContentMapper.selectOne(wrapper);
                //重新赋值文章内容
                apArticleContent.setContent(articleDto.getContent());
                apArticleContentMapper.updateById(apArticleContent);
                //修改文章内容时才会触发异步调用，保存文章模板
                articleFreemarkerService.buildArticleToMinio(apArticle, articleDto.getContent());
            }
            if (articleDto.getApArticleConfig() != null) {
                //重新赋值文章配置
                ApArticleConfig apArticleConfig = articleDto.getApArticleConfig();
                LambdaUpdateWrapper<ApArticleConfig> wrapper = new LambdaUpdateWrapper<>();
                //如果设置自媒体端文章上下架，app端将发布的文章设置为上下架
                if (articleDto.getApArticleConfig().getIsDown() != null) {
                    wrapper.eq(ApArticleConfig::getArticleId, apArticleConfig.getArticleId())
                            .set(ApArticleConfig::getIsDown, apArticleConfig.getIsDown());
                    int update = apArticleConfigMapper.update(apArticleConfig, wrapper);
                    if (update > 0) {
                        if (apArticleConfig.getIsDown() == 1) {
                            //下架
                            kafkaTemplate.send("delete-article-topic", JSON.toJSONString(articleDto.getApArticleConfig().getArticleId()));
                        } else if (apArticleConfig.getIsDown() == 0) {
                            //上架
                            //获取文章信息
                            LambdaQueryWrapper<ApArticle> wrapper2 = new LambdaQueryWrapper<>();
                            wrapper2.eq(ApArticle::getId, apArticleConfig.getArticleId());
                            ApArticle apArticle1 = apArticleMapper.selectOne(wrapper2);
                            //获取文章内容信息
                            LambdaQueryWrapper<ApArticleContent> wrapper3 = new LambdaQueryWrapper<>();
                            wrapper3.eq(ApArticleContent::getArticleId, apArticleConfig.getArticleId());
                            ApArticleContent apArticleContent = apArticleContentMapper.selectOne(wrapper3);
                            //定义封装对象，用与向es索引库添加文章信息
                            SearchArticleVo searchArticleVo = new SearchArticleVo();
                            BeanUtils.copyProperties(apArticle1, searchArticleVo);
                            searchArticleVo.setContent(apArticleContent.getContent());
                            //当上架时重新向es中插入文章信息
                            kafkaTemplate.send("insert-article-topic", JSON.toJSONString(searchArticleVo));
                        }

                    }
                }
                //如果将自媒体文章下架，app端将发布的文章设置为已删除
                if (articleDto.getApArticleConfig().getIsDelete() != null) {
                    wrapper.eq(ApArticleConfig::getArticleId, apArticleConfig.getArticleId())
                            .set(ApArticleConfig::getIsDelete, apArticleConfig.getIsDelete());
                    int update = apArticleConfigMapper.update(apArticleConfig, wrapper);
                    if (update > 0) {
                        kafkaTemplate.send("delete-article-topic", JSON.toJSONString(articleDto.getApArticleConfig().getArticleId()));
                    }
                }

            }

        }
        //返回文章id
        return ResponseResult.okResult(apArticle.getId());
    }

    /**
     * 加载文章行为-数据回显
     * @param dto
     * @return
     */
    @Override
    public ResponseResult loadArticleBehavior(ArticleInfoDto dto) {
        //检验是否登录
        String userId = AppThreadLocalUtil.getUser().getId().toString();
        if(StringUtils.isBlank(userId)){
            return ResponseResult.errorResult(AppHttpCodeEnum.NEED_LOGIN);
        }
        //检验参数
        if(dto==null){
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID);
        }
        ApArticle apArticle = apArticleMapper.selectById(dto.getArticleId());
        //得到redis中该文章的所有用户行为操作
        String likeKey = BeHaviorConstants.LIKE_ARTICLE + dto.getArticleId()+":";
        String unlikeKey = BeHaviorConstants.UNLIKE_ARTICLE + dto.getArticleId()+":";
        String collectKey = BeHaviorConstants.COLLECTION_ARTICLE + dto.getArticleId()+":";
        String followKey = BeHaviorConstants.FOLLOW_AUTHOR + dto.getAuthorId()+":";
        String readKey = BeHaviorConstants.READ_ARTICLE + dto.getArticleId()+":";
        //向前端响应该文章有没有被该用户点赞，关注，不喜欢，收藏
        ArticleInfoVo articleInfoVo = ArticleInfoVo
                .builder()
                .islike(cacheService.hGet(likeKey, userId) != null)
                .isunlike(cacheService.hGet(unlikeKey, userId) != null)
                .iscollection(cacheService.hGet(collectKey, userId) != null)
                .isfollow(cacheService.hGet(followKey, userId) != null)
                .build();
        //获取redis中该文章被阅读，被点赞，被评论，被收藏的次数存入该文章信息中
        //BehavioralActions(likeKey,readKey,collectKey,followKey,unlikeKey,apArticle);

        return ResponseResult.okResult(articleInfoVo);
    }

    /**
     * 获取redis中该文章被阅读，被点赞，被评论，被收藏的次数存入该文章信息中
     * @param likeKey
     * @param readKey
     * @param collectKey
     * @param followKey
     * @param unlikeKey
     * @param apArticle
     */
    private void BehavioralActions(String likeKey, String readKey, String collectKey, String followKey, String unlikeKey,ApArticle apArticle) {
        //被阅读
        Integer readCount=0;
        Map<Object,Object> map=cacheService.hGetAll(readKey);
        for (Object key : map.keySet()) {
            ReadBehaviorDto readBehaviorDto = JSON.parseObject((String) map.get(key), ReadBehaviorDto.class);
            readCount+=readBehaviorDto.getCount();
        }
        apArticle.setViews(readCount);
        updateById(apArticle);
        //被点赞
        Integer likeSize = cacheService.hGetAll(likeKey).size();
        apArticle.setLikes(likeSize);
        updateById(apArticle);
        //TODO:被评论

        //被收藏
        Integer collectionSize = cacheService.hGetAll(collectKey).size();
        apArticle.setCollection(collectionSize);
        updateById(apArticle);
    }

    /**
     * 文章收藏功能
     * @param dto
     * @return
     */
    @Override
    public ResponseResult collectionBehavior(CollectionBehaviorDto dto) {
        String userId = AppThreadLocalUtil.getUser().getId().toString();
        if (StringUtils.isEmpty(userId)||userId.equals("0")) {
            return ResponseResult.errorResult(AppHttpCodeEnum.NEED_LOGIN);
        }
        if(dto==null){
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID);
        }
        ApArticle apArticle = apArticleMapper.selectById(dto.getEntryId());
        //组装key
        UpdateArticleMess updateArticleMess = new UpdateArticleMess();
        updateArticleMess.setArticleId(dto.getEntryId());
        updateArticleMess.setType(UpdateArticleMess.UpdateArticleType.COLLECTION);
        String key="";
        if(dto.getType()==0){
            //收藏文章
            key=BeHaviorConstants.COLLECTION_ARTICLE+dto.getEntryId()+":";
        }else{
            //收藏动态
            key=BeHaviorConstants.COLLECTION_DYNAMIC+dto.getEntryId()+":";
        }
        //判断操作
        if(dto.getOperation()==0){
            //收藏
            if(cacheService.hGet(key,userId)!=null){
                return ResponseResult.errorResult(AppHttpCodeEnum.SERVER_ERROR,"您已收藏，请勿重复操作");
            }
            cacheService.hPut(key,userId,JSON.toJSONString(dto));
//            apArticle.setCollection(apArticle.getCollection()+1);
//            apArticleMapper.updateById(apArticle);
            updateArticleMess.setAdd(1);
        }else{
            if(cacheService.hGet(key,userId)==null){
                return ResponseResult.errorResult(AppHttpCodeEnum.SERVER_ERROR,"您未收藏，无法取消");
            }
            cacheService.hDelete(key,userId);
//            apArticle.setCollection(apArticle.getCollection()-1);
//            apArticleMapper.updateById(apArticle);
            updateArticleMess.setAdd(-1);
        }
        kafkaTemplate.send(HotArticleConstants.HOT_ARTICLE_SCORE_TOPIC, JSON.toJSONString(updateArticleMess));
        return ResponseResult.okResult(AppHttpCodeEnum.SUCCESS);
    }

    /**
     * 更新文章成绩
     * @param mess
     */
    @Override
    public void updateArticleScore(ArticleVisitStreamMess mess) {
        //更新文章用户行为
        ApArticle apArticle = apArticleMapper.selectById(mess.getArticleId());
        apArticle.setViews(apArticle.getViews()==null?0:apArticle.getViews() + mess.getView());
        apArticle.setLikes(apArticle.getLikes()==null?0:apArticle.getLikes() + mess.getLike());
        apArticle.setCollection(apArticle.getCollection()==null?0:apArticle.getCollection() + mess.getCollect());
        apArticle.setComment(apArticle.getComment()==null?0:apArticle.getComment() + mess.getComment());
        updateById(apArticle);
        //计算当前文章的分值
        HotApArticleVo hotApArticleVo = new HotApArticleVo();
        BeanUtils.copyProperties(apArticle, hotApArticleVo);
        Integer score = 0;
        if (apArticle.getViews() != null && apArticle.getViews() > 0) {
            score += apArticle.getViews();
        }
        if (apArticle.getLikes() != null && apArticle.getLikes() > 0) {
            score += apArticle.getLikes() * ArticleConstants.HOT_ARTICLE_LIKE_WEIGHT;
        }
        if (apArticle.getCollection() != null && apArticle.getCollection() > 0) {
            score += apArticle.getCollection() * ArticleConstants.HOT_ARTICLE_COLLECTION_WEIGHT;
        }
        if (apArticle.getComment() != null && apArticle.getComment() > 0) {
            score += apArticle.getComment() * ArticleConstants.HOT_ARTICLE_COMMENT_WEIGHT;
        }
        hotApArticleVo.setScore(score * 3L);
        //替换当前文章对应频道的热点数据
        List<HotApArticleVo> hotApArticleVos = JSON
                .parseArray(cacheService.get(ArticleConstants.HOT_ARTICLE_FIRST_PAGE + apArticle.getChannelId()),
                HotApArticleVo.class);
        replaceHotArticle(hotApArticleVos,hotApArticleVo,apArticle.getChannelId().toString());
        //替换推荐对应的热点数据
        List<HotApArticleVo> DefaultHotApArticleVos = JSON
                .parseArray(cacheService.get(ArticleConstants.HOT_ARTICLE_FIRST_PAGE + ArticleConstants.DEFAULT_TAG),
                        HotApArticleVo.class);
        replaceHotArticle(hotApArticleVos,hotApArticleVo,apArticle.getChannelId().toString());
    }
    /**
     * 分页查询所有已发布app端文章信息
     * @param articlePageDto
     * @return
     */
    @Override
    public ResponseResult findAll(ArticlePageDto articlePageDto) {
        String beginDate = articlePageDto.getBeginDate();
        String endDate = articlePageDto.getEndDate();
        IPage<ApArticle> page=new Page<>(articlePageDto.getPage(),articlePageDto.getSize());
        LambdaQueryWrapper<ApArticle> queryWrapper = new LambdaQueryWrapper<>();
        if(beginDate!=null&&endDate!=null){
            queryWrapper.between(ApArticle::getPublishTime,beginDate,endDate);
        }
        queryWrapper.orderByDesc(ApArticle::getPublishTime);
        IPage<ApArticle> apArticleIPage = apArticleMapper.selectPage(page, queryWrapper);
        PageResponseResult pageResponseResult = new PageResponseResult();
        pageResponseResult.setData(JSON.toJSONString(apArticleIPage.getRecords()));
        pageResponseResult.setCurrentPage((int) apArticleIPage.getCurrent());
        pageResponseResult.setSize((int) apArticleIPage.getSize());
        pageResponseResult.setTotal((int) apArticleIPage.getTotal());
        String jsonString = JSON.toJSONString(pageResponseResult);
        return ResponseResult.okResult(jsonString);
    }

    /**
     * 替换每个频道中分值最小的热点文章
     * @param hotApArticleVoList 频道所有热点文章/推荐所有热点文章
     * @param hotApArticleVo 当前需要插入的文章
     * @param channel 频道名称
     */
    private void replaceHotArticle(List<HotApArticleVo> hotApArticleVoList,HotApArticleVo hotApArticleVo,String channel) {
        //判断缓存中是否有该文章，如果有则只需要更新分值(设置标志位，如果判断结果为”真“，则跳过插入/替换新文章)
        boolean flag = true;
        for (HotApArticleVo hotapArticleVo : hotApArticleVoList) {
            if(hotapArticleVo.getId().equals(hotApArticleVo.getId())){
                hotapArticleVo.setScore(hotApArticleVo.getScore());
                flag=false;
                //如果更改成功则不需要继续查找直接退出循环
                break;
            }
        }
        //如果没有该文章，则需要考虑总文章数是否达到30.若没有达到，直接插入;如果达到，则需要替换分值最小的文章
        if(flag){
            if(hotApArticleVoList.size()==30){
                //因为以前已经根据文章分数排序，所以最后的就是分数最小的
                HotApArticleVo lastHotArticle = hotApArticleVoList.get(hotApArticleVoList.size() - 1);
                //判断最后一个热点文章的分数是否小于当前需要插入的文章
                if(lastHotArticle.getScore()<hotApArticleVo.getScore()){
                    //从该频道热点文章中直接删除最后一条文章
                    hotApArticleVoList.remove(lastHotArticle);
                }else{
                    //如果最后一个热点文章的分数大于或等于需要插入的新的文章，则直接退出不插入新的文章
                    return ;
                }
            }
            //最后一篇文章已删除，插入新的文章，并排序后放入redis中
            hotApArticleVoList.add(hotApArticleVo);
        }
        hotApArticleVoList=hotApArticleVoList.stream().sorted(Comparator.comparing(HotApArticleVo::getScore).reversed()).collect(Collectors.toList());
        cacheService.set(ArticleConstants.HOT_ARTICLE_FIRST_PAGE+channel,JSON.toJSONString(hotApArticleVoList));
    }
}




